﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;

namespace Input
{
    /// <summary>
    /// Used to control mouse movement and keep a state of the mouse for the game
    /// </summary>
    public class Mouse
    {

        /// <summary>
        /// Contains the previous state of the mouse, we'll compare it to a new state later
        /// </summary>
        private MouseState prevState;

        /// <summary>
        /// Contains the new state of the mouse, this updated when you call Mouse.Update
        /// </summary>
        private MouseState newState;

        /// <summary>
        /// Contains the mouse cursor texture to render to screen on draw
        /// </summary>
        private Texture2D _mouseCursor;

        /// <summary>
        /// Contains the current mouse _position before moving the mouse
        /// </summary>
        private Vector2 _position;

        /// <summary>
        /// Contains the rectangle where we must _constraint the mouse to
        /// </summary>
        private Rectangle _constraintTo;

        /// <summary>
        /// Should the engine _constraint the mouse to that _position?
        /// </summary>
        private Boolean _constraint = false;

        /// <summary>
        /// Forces the mouse to stick to cursor position even when constrainted?
        /// </summary>
        private Boolean _forceHardwardMousePosition;

        /// <summary>
        /// Creates a mouse
        /// </summary>
        public Mouse()
        {

            //Save the state of the mouse right off the start
            prevState = Microsoft.Xna.Framework.Input.Mouse.GetState();

        }

        /// <summary>
        /// Creates a mouse with a default position and constraint
        /// </summary>
        public Mouse(Vector2 DefaultPosition, Rectangle DefaultConstraint)
        {

            //Save the state of the mouse right off the start
            prevState = Microsoft.Xna.Framework.Input.Mouse.GetState();

            //Save the settings
            this._position = DefaultPosition;
            this._constraintTo = DefaultConstraint;
            this._constraint = true;

            //Set the real mouse there
            Microsoft.Xna.Framework.Input.Mouse.SetPosition((int)_position.X, (int)_position.Y);

        }

        /// <summary>
        /// Creates a mouse with a default cursor texture to use when rendering the mouse
        /// </summary>
        public Mouse(Texture2D cursorTexture)
        {

            //Save the state of the mouse right off the start
            prevState = Microsoft.Xna.Framework.Input.Mouse.GetState();

            //Save the settings
            this._mouseCursor = cursorTexture;

        }

        /// <summary>
        /// Creates a mouse with a default position, constraint and cursor texture to use when rendering the mouse
        /// </summary>
        public Mouse(Texture2D cursorTexture, Vector2 DefaultPosition, Rectangle DefaultConstraint)
        {

            //Save the state of the mouse right off the start
            prevState = Microsoft.Xna.Framework.Input.Mouse.GetState();

            //Save the settings
            this._mouseCursor = cursorTexture;
            this._position = DefaultPosition;
            this._constraintTo = DefaultConstraint;
            this._constraint = true;

            //Set the real mouse there
            Microsoft.Xna.Framework.Input.Mouse.SetPosition((int)_position.X, (int)_position.Y);

        }

        /// <summary>
        /// Begins a cycle by getting the new mouse state
        /// </summary>
        public void Begin()
        {
            newState = Microsoft.Xna.Framework.Input.Mouse.GetState();
        }

        /// <summary>
        /// Updates the mouse _position based on the _constraint and the movement of the mouse
        /// </summary>
        public void Update()
        {

            //Update the mouse _position
            _position.X += this.XMovement;
            _position.Y += this.YMovement;

            //_constraint?
            if (this._constraint == true)
            {
                //_constraint the Xs
                _position.X = (_position.X < this._constraintTo.X ? this._constraintTo.X : _position.X);
                _position.X = (_position.X > this._constraintTo.X + this._constraintTo.Width ? this._constraintTo.X + this._constraintTo.Width : _position.X);
                
                //_constraint the Ys
                _position.Y = (_position.Y < this._constraintTo.Y ? this._constraintTo.Y : _position.Y);
                _position.Y = (_position.Y > this._constraintTo.Y + this._constraintTo.Height ? this._constraintTo.Y + this._constraintTo.Height : _position.Y);
            }

            //Force mouse to stick to cursor position?
            if (this._forceHardwardMousePosition)
            {
                //Set the real mouse there and refresh the state or the system will freak out since we moved it and it is not where it should anymore...
                Microsoft.Xna.Framework.Input.Mouse.SetPosition((int)_position.X, (int)_position.Y);
                newState = Microsoft.Xna.Framework.Input.Mouse.GetState();
            }

        }

        /// <summary>
        /// Ends a cycle by saving the new mouse state to the previous making it useless to poll
        /// </summary>
        public void End()
        {
            prevState = newState;
        }

        /// <summary>
        /// Returns the X movement on this cycle
        /// </summary>
        public int XMovement
        {
            get
            {
                return newState.X - prevState.X;
            }
        }

        /// <summary>
        /// Returns the Y movement on this cycle
        /// </summary>
        public int YMovement
        {
            get
            {
                return newState.Y - prevState.Y;
            }
        }

        /// <summary>
        /// Returns the Wheel movement on this cycle
        /// </summary>
        public int WheelMovement
        {
            get
            {
                return newState.ScrollWheelValue - prevState.ScrollWheelValue;
            }
        }

        /// <summary>
        /// Returns if a left click has just begun
        /// </summary>
        public bool LeftClickBegun
        {
            get
            {
                return prevState.LeftButton == ButtonState.Released && newState.LeftButton == ButtonState.Pressed;
            }
        }

        /// <summary>
        /// Returns if a left click has just completed
        /// </summary>
        public bool LeftClickCompleted
        {
            get
            {
                return prevState.LeftButton == ButtonState.Pressed && newState.LeftButton == ButtonState.Released;
            }
        }

        /// <summary>
        /// Returns if a middle click has just begun
        /// </summary>
        public bool MiddleClickBegun
        {
            get
            {
                return prevState.MiddleButton == ButtonState.Released && newState.MiddleButton == ButtonState.Pressed;
            }
        }

        /// <summary>
        /// Returns if a middle click has just completed
        /// </summary>
        public bool MiddleClickCompleted
        {
            get
            {
                return prevState.MiddleButton == ButtonState.Pressed && newState.MiddleButton == ButtonState.Released;
            }
        }

        /// <summary>
        /// Returns if a right click has just begun
        /// </summary>
        public bool RightClickBegun
        {
            get
            {
                return prevState.RightButton == ButtonState.Released && newState.RightButton == ButtonState.Pressed;
            }
        }

        /// <summary>
        /// Returns if a right click has just completed
        /// </summary>
        public bool RightClickCompleted
        {
            get
            {
                return prevState.RightButton == ButtonState.Pressed && newState.RightButton == ButtonState.Released;
            }
        }

        /// <summary>
        /// Gets or sets the current position of the mouse on the interface
        /// </summary>
        public Vector2 CurrentPosition
        {
            get
            {
                return this._position;
            }
            set
            {
                this._position = value;
            }
        }

        /// <summary>
        /// Enables or disables the mouse constraint, remember to set a constraint rectangle!
        /// </summary>
        public Boolean Constraint
        {
            get
            {
                return this._constraint;
            }
            set
            {
                this._constraint = value;
            }

        }

        /// <summary>
        /// Enables or disables the hardward mouse position constraint!
        /// </summary>
        public Boolean ForceHardwareMousePosition
        {
            get
            {
                return this._forceHardwardMousePosition;
            }
            set
            {
                this._forceHardwardMousePosition = value;
            }

        }

        /// <summary>
        /// Gets/Sets the constrainted area for the mouse, remember to activate the constraint flag!
        /// </summary>
        public Rectangle ConstraintTo
        {
            get
            {
                return this._constraintTo;
            }
            set
            {
                this._constraintTo = value;
            }
        }

        /// <summary>
        /// Gets/Sets the mouse cursor texture to render to the sprite batch
        /// </summary>
        public Texture2D MouseCursor
        {
            get
            {
                return this._mouseCursor;
            }
            set
            {
                this._mouseCursor = value;
            }
        }

        /// <summary>
        /// Draws the mouse cursor on the spriteBatch based on internal position
        /// </summary>
        /// <param name="spriteBatch"></param>
        public void Draw(SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(this._mouseCursor, this._position, Color.White);
        }

    }
}
